tg-me.com/golang_books/961
Last Update:
Как часто вы ловите себя на вопросе: «Как лучше организовать структуру репозитория Go‑проекта?» В беседах на Gopher Slack и телеграм чатах подобные вопросы возникают вновь и вновь.
Впрочем, в сети так много устаревших или чрезмерно запутанных гайдов, что порой сложно найти по-настоящему удручающе простое решение.
Одна из краеугольных идей философии Go — стремление к максимальной простоте.
Начните с малого: пишите функциональный код, не создавая сложную иерархию каталогов до тех пор, пока в этом нет реальной нужды.
Что говорит официальная документация
На сайте go.dev можно найти рекомендации от авторов языка:
«В больших проектах или монолитных приложениях бывает полезно вынести часть функциональности в поддерживающие пакеты, помещая их в каталог internal. Код внутри internal/ недоступен для стороннего импорта, что позволяет нам без опасений рефакторить API и перестраивать структуру».
Заметьте, слова — «больших» и «бывает» — подчёркнуты не случайно.
Большинству небольших или средних проектов вовсе не нужен internal/: достаточно просто не экспортировать те функции, которые не предназначены для внешнего использования.
Идея «сначала создайте internal/, потом думайте о дизайне» кажется чрезмерной. Но зачастую, разумнее сперва выпустить полезный функционал, а уже потом, по мере роста проекта, скорректировать структуру.
Что не стоит копировать вслепую
Наверняка вы слышали о репозитории «golang-standards/project-layout», который многие называют «стандартом». На деле это скорее набор идей, вокруг которых бурно дискутируют, нежели непреложное правило.
Не позволяйте чужим конвенциям диктовать ваш рабочий процесс: возможность импортировать из internal/ или раскладывать всё по папкам cmd/, pkg/ и т. д. — далеко не обязательный шаблон для каждого проекта.
Рекомендации из практики
Пакет main
Если ваш проект — это одно единственное приложение, вполне логично держать main.go в корне. Команда
go install github.com/you/project@latest
сработает без лишних телодвижений. Если же вы развиваете и библиотеку, и отдельный исполняемый файл, можно вынести main в подпапку (например, app/) — но не более того.
Каталог internal/
Применяйте его лишь в том случае, когда ваш код действительно потребляют десятки сторонних проектов. Для большинства библиотек и приложений довольно размещать «внутренние» пакеты там, где они логически принадлежат, просто не экспортируя нежелательные символы.
Каталог pkg/
Когда-то pkg/ помогал отделять библиотеки от «прочего» кода. Теперь, после появления internal/, он утратил былую нужность. Любой пакет из pkg/ можно без труда перенести в корень, не нарушив логику проекта.
Утилиты util/, common/, shared/
«Утильные» каталоги зачастую превращаются в свалку беспорядочных функций. Лучше дать такому набору ясное название или разместить функции непосредственно рядом с их точкой использования.
Не множьте пакеты без надобности
В Go несколько файлов в одном пакете — не проблема. Не выделяйте новый пакет ради нескольких строк кода: это усложняет навигацию и повышает риск непреднамеренных циклических зависимостей.
Примеры
🔸 github.com/fortio/ — сервер, библиотеки и CLI в одном репозитории
🔸 github.com/fortio/proxy — отдельный HTTP‑прокси
🔸 github.com/fortio/multicurl — консольный инструмент
🔸 github.com/fortio/terminal — два пакета + CLI
🔸 github.com/grol-io/grol — Go с WebAssembly
Сосредоточтесь на действительно важных вещах — написании кода.